#include <stdx/all.h>
#include <json/json.h>
#include <http/HttpHelper.h>

class MainApplication : public Application
{
public:
	bool main()
	{
		if (GetCmdParam("--help") || GetCmdParam("?"))
		{
			puts("  parameter list");
			puts("---------------------------");
			puts("  -u : route link");
			puts("  -p : grasp period");
			puts("  -l : print record");
			puts("  -d : ouput directory");
			puts("---------------------------");
			puts("");

			return true;
		}

		LogThread::Instance()->init("log");
		
		if (GetCmdParam("-l")) LogThread::Instance()->setLogFlag(2);

		int delay = 5;
		const char* link = GetCmdParam("-u");
		const char* path = GetCmdParam("-d");
		const char* period = GetCmdParam("-p");

		if (link == NULL || *link == 0)
		{
			ColorPrint(eRED, "missing parameter[%s]\n", "-u");

			return false;
		}

		if (path == NULL || *path == 0) path = "etc";

		if (period && *period) delay = stdx::atoi(period);

		if (delay < 5) delay = 5;

		path::mkdir(path);

		HttpDataNode param;
		string upstreamdest;
		string locationdest;

		param.setValue("format", "nginx");

		stdx::format(upstreamdest, "%s/upstream.conf", path);
		stdx::format(locationdest, "%s/location.conf", path);

		while (true)
		{
			sleep(1);

			long now = time(NULL);
			SmartBuffer data = HttpHelper::GetResult(link, param);
			
			if (data.isNull())
			{
				LogTrace(eERR, "grasp content[%s] failed", link);

				continue;
			}

			string upstream;
			string location;
			vector<string> vec;
			JsonElement json(data.str());
			
			if (json.isObject())
			{
				upstream = json["upstream"].asString();
				location = json["location"].asString();
			}
			
			if (upstream.empty() || location.empty())
			{
				LogTrace(eERR, "grasp content[%s] failed", link);

				continue;
			}

			string upstreampath;
			string locationpath;

			stdx::format(upstreampath, "%s/upstream.%ld", path, now);
			stdx::format(locationpath, "%s/location.%ld", path, now);

			auto save = [&](){
				File upstreamfile;
				File locationfile;

				if (upstreamfile.open(upstreampath, "w+") && locationfile.open(locationpath, "w+"))
				{
					if (upstreamfile.write(upstream.c_str(), upstream.length()) < 0) return XG_IOERR;
					if (locationfile.write(location.c_str(), location.length()) < 0) return XG_IOERR;

					return XG_OK;
				}
				
				return XG_IOERR;
			};
			
			auto cleanup = [&](){
				int mincnt = 10;

				if (vec.size() > mincnt)
				{
					std::sort(vec.begin(), vec.end());

					for (auto& item : vec)
					{
						if (++mincnt > vec.size()) break;

						LogTrace(eINF, "remove file[%s] success", item.c_str());

						path::remove(item);
					}
				}
			};
			
			if (stdx::FindFile(vec, path, "upstream.*") < 0)
			{
				LogTrace(eERR, "cleanup upstream history failed");

				continue;
			}

			cleanup();

			if (stdx::FindFile(vec, path, "location.*") < 0)
			{
				LogTrace(eERR, "cleanup location history failed");

				continue;
			}

			cleanup();

			string destupstream;
			string destlocation;

			stdx::GetFileContent(destupstream, upstreamdest);
			stdx::GetFileContent(destlocation, locationdest);

			if (upstream == destupstream && location == destlocation)
			{
				LogTrace(eINF, "skip nginx update");
			}
			else
			{
				if (save() < 0)
				{
					LogTrace(eERR, "download location failed");

					continue;
				}
				
				string upstreamback;
				string locationback;

				stdx::format(upstreamback, "%s/upstream.%ld", path, now + 1);
				stdx::format(locationback, "%s/location.%ld", path, now + 1);

				path::rename(upstreamdest, upstreamback);
				path::rename(locationdest, locationback);
				path::rename(upstreampath, upstreamdest);
				path::rename(locationpath, locationdest);

				destupstream.clear();
				destlocation.clear();

				stdx::GetFileContent(destupstream, upstreamdest);
				stdx::GetFileContent(destlocation, locationdest);

				if (upstream == destupstream && location == destlocation)
				{
					LogTrace(eINF, "update nginx success");
					
					System("nginx -s reload");
				}
				else
				{
					LogTrace(eERR, "update nginx failed");
				}
			}

			sleep(delay);
		}

		LogThread::Instance()->wait();

		return true;
	}
};

START_APP(MainApplication)